package cn.shop.merchant.controller.user;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.shop.merchant.config.LogAsync;
import cn.shop.merchant.model.logs.MerchantLog;
import cn.shop.merchant.model.user.AdminMenuVO;
import cn.shop.merchant.model.user.UserInfoForSession;
import cn.shop.merchant.req.AutoLoginInfoReq;
import cn.shop.merchant.req.LoginInfoReq;
import cn.shop.merchant.service.*;
import cn.shop.merchant.util.IpUtil;
import cn.shop.thiredparty.entity.SmsSendResult;
import cn.shop.thiredparty.entity.SmsSendStatus;
import cn.shop.thiredparty.service.ThiredpartyService;
import cn.shop.transcation.ipWhitelistModule.entity.IpWhitelist;
import cn.shop.transcation.merchantUser.mapper.entity.MerchantUser;
import cn.shop.utils.ApiCode;
import cn.shop.utils.ApiResult;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.Constant;
import cn.shop.utils.constant.Regular;
import cn.shop.utils.constant.SMSVerificationCode;
import cn.shop.utils.enums.OperationType;
import cn.shop.validation.Regist;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.shop.utils.ApiResult.fail;
import static cn.shop.utils.constant.Constant.UID;

/**
 * 登录相关
 *
 * @author : 李一涵
 * @date : Created in 2021-11-12 10:06
 * @description : 登录相关
 */
@RestController
@RequestMapping("/user")
public class LoginController {

	private final static Logger LOG = LoggerFactory.getLogger(LoginController.class);

	@Resource
	private MerchantUserService userService;

	@Resource
	private IpWhitelistService ipWhitelistService;

	@Resource
	private MerchantPermissionService permissionService;

	@Resource
	private OrderService orderService;
	@Resource
	private LogAsync logAsync;
	@Resource
	private InboxService inboxService;
	@Resource
	private RedisTemplate<String, String> redisTemplate;

	@Resource
	private ThiredpartyService thiredpartyService;
	private static final int EXPIRE_MIN = 5;


	/**
	 * 登录
	 *
	 * @param loginInfo    登录信息
	 * @param captchaToken 验证码token
	 * @return 返回结果
	 */
	@PostMapping("/login")
	public ApiResult<Map<String, Object>> login(@RequestBody LoginInfoReq loginInfo, @RequestHeader(required = false, name = "Captcha-Token") String captchaToken) {
		/* 验证码开始 */

		SaSession tokenSession = StpUtil.getTokenSession();
		Object attribute = tokenSession.get(Constant.CAPTCHA_SESSION_KEY);
		if (attribute == null) {
			return ApiResult.fail("验证码已失效,请刷新页面后重试!");
		}
		if (!StrUtil.equalsIgnoreCase((CharSequence) attribute, loginInfo.getCode())) {
			return ApiResult.fail("验证码不正确!");
		}
		tokenSession.delete(Constant.CAPTCHA_SESSION_KEY);
		/* 验证码结束 */
		Assert.notBlank(loginInfo.getPassword(), "密码不能为空");
		MerchantUser user = userService.login(loginInfo.getAccount(), loginInfo.getPassword());
		if (user == null) {
			return ApiResult.fail("账户或密码错误");
		}
		if (user.getState() == 1) {
			return ApiResult.fail("账号暂未审核成功，请审核成功后登录");
		}
		if (user.getState() == 3) {
			return ApiResult.fail("您的账号已被禁用，请联系管理员");
		}
		//判断当前登录是否在白名单内
		LambdaQueryWrapper<IpWhitelist> ipWhitelistLambdaQueryWrapper = Wrappers.lambdaQuery();
		ipWhitelistLambdaQueryWrapper.eq(IpWhitelist::getUserId, user.getUid());
		List<IpWhitelist> ipWhitelistList = ipWhitelistService.list(ipWhitelistLambdaQueryWrapper);
		String clientIp = IpUtil.getClientIp();
		if (ipWhitelistList.size() > 0) {
			boolean b = ipWhitelistList.stream().noneMatch(e -> e.getIp().equals(clientIp));
			if (b) {
				return ApiResult.fail("当前设备无法登录");
			}
		}
		Long userId = user.getId();
		StpUtil.login(userId);
		MerchantLog backLog = new MerchantLog();
		backLog.setCreateId(userId);
		backLog.setAccount(user.getAccount());
		backLog.setDetails(StrUtil.format("登录成功,账户 : {} , 登录ip : {}", user.getAccount(), clientIp));
		backLog.setName(user.getName());
		backLog.setType(OperationType.LOGIN);
		backLog.setUid(user.getUid() == 0 ? user.getId() : user.getUid());
		logAsync.operationLog(backLog);
		StpUtil.login(user.getId());
		SaTokenInfo token = StpUtil.getTokenInfo();
		StpUtil.getSession().set(Constant.USER, new UserInfoForSession(user.getId(), user.getAccount(), user.getName()))
				.set(UID, user.getUid() == 0 ? user.getId() : user.getUid());
		Map<String, Object> data = Putif.start()
				.anyway("tokenName", token.getTokenName())
				.anyway("username", user.getAccount())
				.anyway("token", token.getTokenValue())
				.anyway("isParent", user.getUid() == 0)
				.get();
		return ApiResult.success("登陆成功", data);
	}

	@GetMapping("/blogin/{uid}")
	public ApiResult<Map<String, Object>> backLogin(@PathVariable long uid) {
		MerchantUser user = userService.getById(uid);
		if (user == null) {
			return ApiResult.fail("登录失败");
		}
		Long userId = user.getId();
		StpUtil.login(userId);
		SaTokenInfo token = StpUtil.getTokenInfo();
		StpUtil.getSession().set(Constant.USER, new UserInfoForSession(user.getId(), user.getAccount(), user.getName()))
				.set(UID, user.getUid() == 0 ? user.getId() : user.getUid());
		Map<String, Object> data = Putif.start()
				.anyway("tokenName", token.getTokenName())
				.anyway("username", user.getAccount())
				.anyway("token", token.getTokenValue())
				.anyway("isParent", user.getUid() == 0)
				.get();
		return ApiResult.success("登陆成功", data);
	}

	/**
	 * 自动登录
	 *
	 * @param autoLoginInfoReq 登录信息
	 * @return 返回结果
	 */
	@PostMapping("/automaticLogin")
	public ApiResult<Map<String, Object>> automaticLogin(@RequestBody AutoLoginInfoReq autoLoginInfoReq) {
		return userService.automaticLogin(autoLoginInfoReq);
	}

	/**
	 * 获取图形验证码
	 *
	 */
	@GetMapping("/verificationCode")
	public void verificationCode(HttpServletResponse response) {
		//定义图形验证码的长、宽、验证码字符数、干扰元素个数
		LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 32, 4, 4);
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			Cookie cookie = new Cookie("verificationCode", captcha.getCode());
			cookie.setPath("/");
			cookie.setMaxAge(1800);
			response.addCookie(cookie);
			SaSession tokenSession = StpUtil.getTokenSession();
			tokenSession.delete(Constant.CAPTCHA_SESSION_KEY);
			tokenSession.set(Constant.CAPTCHA_SESSION_KEY, captcha.getCode());
			//图形验证码写出，可以写出到文件，也可以写出到流
			captcha.write(outputStream);
			outputStream.flush();
		} catch (IOException ignored) {
		}
	}

	/**
	 * 获取当前登录人的按钮权限
	 *
	 * @author jy
	 * @since 2022/1/18 10:46
	 */
	@GetMapping("/btnPermission")
	public ApiResult<List<String>> getBtnPermission() {
		return ApiResult.success(userService.getAllBtnPermissionByUserId());
	}

	/**
	 * 查询当前登录人信息
	 *
	 * @return
	 * @author jy
	 * @since 2022/1/18 10:47
	 */
	@GetMapping("/userInfo")
	public ApiResult<MerchantUser> userInfo() {
		return ApiResult.success(userService.getById(StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId()));
	}

	/**
	 * 获取当前登录人未读站内信数量
	 *
	 * @return 返回结果
	 */
	@GetMapping("/unreads")
	public ApiResult<Long> unreads() {
		return ApiResult.success(inboxService.getUnreadCount(StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId()));
	}

	/**
	 * 获取站内信列表
	 *
	 * @return 返回结果
	 */
	@GetMapping("/inboxes")
	public ApiResult<List<Map<String, Object>>> inboxes() {
		return ApiResult.success(inboxService.inboxList(StpUtil.getSession().getModel(Constant.USER, UserInfoForSession.class).getId()));
	}

	/**
	 * 设置已读
	 *
	 * @param ids 编号
	 * @return 返回结果
	 */
	@PutMapping("/read")
	public ApiResult<Map<String, Object>> read(@RequestBody List<Long> ids) {
		return ApiResult.result(inboxService.read(ids));
	}

	/**
	 * 获取当前登录人的页面权限
	 *
	 * @author jy
	 * @since 2022/1/18 10:45
	 */
	@GetMapping("/permission")
	public ApiResult<List<String>> getPermissions() {
		return ApiResult.success(userService.getAllPermissionUrlByUserId());
	}

	/**
	 * 登出
	 *
	 * @return 返回结果
	 */
	@GetMapping("/logout")
	public ApiResult<Map<String, Object>> logout() {
		StpUtil.logout();
		return ApiResult.success();
	}

	/**
	 * 检查是否登录
	 *
	 * @return 返回结果
	 */
	@GetMapping("/checkLogin")
	public ApiResult<Map<String, Object>> checkLogin() {
		return ApiResult.success(Collections.singletonMap("res", StpUtil.isLogin()));
	}

	/**
	 * 获取当前登录用户的菜单
	 *
	 * @return 返回结果
	 */
	@GetMapping("/menu")
	public ApiResult<List<AdminMenuVO>> menu() {
		long uid = StpUtil.getSession().getLong(UID);
		return ApiResult.success(permissionService.userPermissionTree(StpUtil.getLoginIdAsLong(), uid));
	}

	/**
	 * 首屏数据
	 *
	 * @return 返回结果
	 */
	@GetMapping("/home")
	public ApiResult<Map<String, Object>> homePage() {
		long uid = StpUtil.getSession().getLong(UID);
		return ApiResult.success(orderService.statusCount(uid));
	}

	/**
	 * 注册商户
	 *
	 * @param user
	 * @return
	 */
	@PostMapping("/regist")
	public ApiResult<Map<String, Object>> regist(@Validated(Regist.class) @RequestBody MerchantUser user) {
		String key = SMSVerificationCode.M_REGIST_KEY + user.getPhone();
		String c = redisTemplate.opsForValue().get(key);
		LOG.info("key={}, redis-code = {}, user-code={}", key, c, user.getCode());
		if (StrUtil.isBlank(c) || !c.equals(user.getCode())) {
			return fail("验证码不正确");
		}
		redisTemplate.delete(key);

		if (userService.hasAccount(user.getAccount())) {
			return ApiResult.fail("当前账户已存在");
		}
		if (StrUtil.isBlank(user.getPassword())) {
			return ApiResult.fail("密码不能为空");
		}
		boolean match = ReUtil.isMatch(Regular.PASSWORD, user.getPassword());
		if (!match) {
			return ApiResult.fail(ApiCode.PARAMETER_ERROR, Regular.PASSWORD_MASSAGE);
		}
		user.setPassword(DigestUtil.bcrypt(user.getPassword()));
		user.setPublicity(15);
		user.setState(1);
		return ApiResult.result(userService.regist(user));
	}

	/**
	 * 通过手机号获取验证码
	 * @param map 手机号
	 * @return 验证码
	 */
	@PostMapping("/regist/verification")
	public ApiResult<Map<String, Object>> registVerification(@RequestBody Map<String, String> map) {
		String code = RandomUtil.randomNumbers(6);
		SmsSendResult result = thiredpartyService.sendSms(code, EXPIRE_MIN, map.get("mobile"));
		if (result != null && result.getStatus() == SmsSendStatus.OK) {
			String key = SMSVerificationCode.M_REGIST_KEY + map.get("mobile");
			redisTemplate.opsForValue().set(key, code, EXPIRE_MIN, TimeUnit.MINUTES);
			return ApiResult.success("验证码发送成功");
		} else {
			return ApiResult.fail("验证码发送失败");
		}
	}

}
